package com.msh.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.google.common.base.Strings;
import com.msh.common.VMSystem;
import com.msh.conf.OrderConfig;
import com.msh.config.TopicConfig;
import com.msh.contract.VendoutReq;
import com.msh.contract.VendoutReqData;
import com.msh.contract.VendoutResp;
import com.msh.contract.server.OrderCheck;
import com.msh.dao.OrderDao;
import com.msh.emq.MqttProducer;
import com.msh.entity.OrderEntity;
import com.msh.feignService.UserService;
import com.msh.feignService.VMService;
import com.msh.service.OrderCollectService;
import com.msh.service.OrderService;
import com.msh.utils.JsonUtil;
import com.msh.viewmodel.*;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.BucketOrder;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.sql.Date;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;

@Service
@Slf4j
public class OrderServiceImpl extends ServiceImpl<OrderDao,OrderEntity> implements OrderService{

    @Autowired
    private MqttProducer mqttProducer;


    @Autowired
    private OrderCollectService orderCollectService;

    @Autowired
    private VMService vmService;

    @Autowired
    private UserService userService;

    @Autowired
    private RestHighLevelClient esClient;

    @Autowired
    private RestHighLevelClient restHighLevelClient;


    @Override
    public boolean payComplete(OrderEntity orderEntity) {
        sendVendout(orderEntity);//发送出货通知
        return true;
    }


    @Override
    public OrderEntity createOrder(CreateOrder createOrder) throws IOException {
        VendingMachineViewModel vm = vmService.getVMInfo(createOrder.getInnerCode());
        SkuViewModel sku = vmService.getSku(createOrder.getInnerCode(),createOrder.getSkuId());
        OrderEntity orderEntity = new OrderEntity();
        if (vm.getNodeAddr() != null) {
            orderEntity.setAddr(vm.getNodeAddr());
        }
        orderEntity.setNodeId(vm.getNodeId());
        orderEntity.setNodeName(vm.getNodeName());
        orderEntity.setSkuId(sku.getSkuId());
        orderEntity.setSkuName(sku.getSkuName());
        orderEntity.setAmount(sku.getRealPrice());
        orderEntity.setClassId(sku.getClassId());
        orderEntity.setPrice(sku.getPrice());
        orderEntity.setBusinessId(vm.getBusinessId());
        orderEntity.setBusinessName(vm.getBusinessName());
        orderEntity.setInnerCode(createOrder.getInnerCode());
        orderEntity.setOpenId(createOrder.getOpenId());
        orderEntity.setPayStatus(VMSystem.PAY_STATUS_NOPAY);
        orderEntity.setRegionId(vm.getRegionId());
        orderEntity.setRegionName(vm.getRegionName());
        orderEntity.setOrderNo(createOrder.getInnerCode()+createOrder.getSkuId()+System.nanoTime());
        //微信支付
        orderEntity.setPayType(createOrder.getPayType());

        orderEntity.setStatus(VMSystem.ORDER_STATUS_CREATE);
        orderEntity.setOwnerId(vm.getOwnerId());

        //合作商分成计算
        PartnerViewModel partner = userService.getPartner(vm.getOwnerId());//获取合作商
        BigDecimal price = new BigDecimal(sku.getRealPrice());
        BigDecimal bill = price.multiply(new BigDecimal(partner.getRatio())).divide(BigDecimal.valueOf(100), 0, RoundingMode.HALF_UP);
        orderEntity.setBill(bill.intValue());//分成金额
        this.save(orderEntity);

        //将订单放入延迟队列中，10分钟后检查支付状态
        OrderCheck orderCheck = new OrderCheck();
        orderCheck.setOrderNo(orderEntity.getOrderNo());
        try {
            mqttProducer.send("$delayed/600/" + OrderConfig.ORDER_DELAY_CHECK_TOPIC,2,orderCheck);
        } catch (JsonProcessingException e) {
            log.error("发送延迟订单失败",e);
        }

        //将订单信息同步到es中
        IndexRequest request = new IndexRequest("order");
        HashMap<String, Object> map = new HashMap<>();
        map.put("id",orderEntity.getId());
        map.put("status", VMSystem.ORDER_STATUS_VENDOUT_SUCCESS);
        map.put("bill",orderEntity.getBill());
        map.put("owner_id",orderEntity.getOwnerId());
        map.put("pay_type", orderEntity.getPayType());
        map.put("order_no", orderEntity.getOrderNo());
        map.put("inner_code", orderEntity.getInnerCode());
        map.put("sku_name", orderEntity.getSkuName());
        map.put("sku_id", orderEntity.getSkuId());
        map.put("pay_status", orderEntity.getPayStatus());
        map.put("business_name", orderEntity.getBusinessName());
        map.put("business_id", orderEntity.getBusinessId());
        map.put("region_id", orderEntity.getRegionId());
        map.put("region_name",orderEntity.getRegionName());
        map.put("price", orderEntity.getPrice());
        map.put("amount",orderEntity.getAmount());
        map.put("addr",orderEntity.getAddr());
        map.put("open_id", orderEntity.getOpenId());
//                mapper.writeValueAsString(order.getCreateTime())
        DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        map.put("create_time", df.format(orderEntity.getCreateTime()));
        map.put("update_time", df.format(orderEntity.getUpdateTime()));
        request.id(orderEntity.getId()+"").source(JsonUtil.serialize(map), XContentType.JSON);
        restHighLevelClient.index(request, RequestOptions.DEFAULT);
        return orderEntity;
    }


    @Override
    public boolean vendoutResult(VendoutResp vendoutResp) {
        OrderEntity orderEntity = new OrderEntity();
        orderEntity.setOrderNo(vendoutResp.getVendoutOrder().getOrderNo());
        UpdateWrapper<OrderEntity> uw = new UpdateWrapper<>();
        LambdaUpdateWrapper<OrderEntity> lambdaUpdateWrapper = uw.lambda();
        lambdaUpdateWrapper.set(OrderEntity::getPayStatus,1);
        if(vendoutResp.getVendoutOrder().isSuccess()){
            lambdaUpdateWrapper.set(OrderEntity::getStatus,VMSystem.ORDER_STATUS_VENDOUT_SUCCESS);
        }else {
            lambdaUpdateWrapper.set(OrderEntity::getStatus,VMSystem.ORDER_STATUS_VENDOUT_FAIL);
        }
        lambdaUpdateWrapper.eq(OrderEntity::getOrderNo,vendoutResp.getVendoutOrder().getOrderNo());

        return this.update(lambdaUpdateWrapper);
    }

 

    @Override
    public OrderEntity getByOrderNo(String orderNo) {
        QueryWrapper<OrderEntity> qw = new QueryWrapper<>();
        qw.lambda()
                .eq(OrderEntity::getOrderNo,orderNo);

        return this.getOne(qw);
    }

    @Override
    public Boolean cancel(String orderNo) {
        var order = this.getByOrderNo(orderNo);
        if(order.getStatus() > VMSystem.ORDER_STATUS_CREATE)
            return true;

        order.setStatus(VMSystem.ORDER_STATUS_INVALID);
        order.setCancelDesc("用户取消");

        return true;
    }


    /**
     *
     * @param orderEntity
     */
    private void sendVendout(OrderEntity orderEntity){
        //OrderEntity orderEntity = this.getByOrderNo(orderNo);

        VendoutReqData reqData = new VendoutReqData();
        reqData.setOrderNo(orderEntity.getOrderNo());
        reqData.setPayPrice(orderEntity.getAmount());
        reqData.setPayType(Integer.parseInt(orderEntity.getPayType()));
        reqData.setSkuId(orderEntity.getSkuId());
        reqData.setTimeout(60);
        reqData.setRequestTime(LocalDateTime.now().format(DateTimeFormatter.ISO_DATE_TIME));

        //向售货机发送出货请求
        VendoutReq req = new VendoutReq();
        req.setVendoutData(reqData);
        req.setSn(System.nanoTime());
        req.setInnerCode(orderEntity.getInnerCode());
        req.setNeedResp(true);
        try {
            mqttProducer.send(TopicConfig.getVendoutTopic(orderEntity.getInnerCode()),2,req);
        } catch (JsonProcessingException e) {
            log.error("send vendout req error.",e);
        }
    }

    @Override
    public Pager<OrderViewModel> search(Integer pageIndex, Integer pageSize, String orderNo, String openId, LocalDate startDate, LocalDate endDate) {
        SearchRequest request = new SearchRequest("order");
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();

        if (!Strings.isNullOrEmpty(orderNo)) {
            boolQueryBuilder.must(QueryBuilders.termQuery("order_no",orderNo));
        }
        if (!Strings.isNullOrEmpty(openId)) {
            boolQueryBuilder.must(QueryBuilders.termQuery("open_id",openId));
        }
        //时间段条件
        if (!StringUtils.isEmpty(startDate) && !StringUtils.isEmpty(endDate)) {
            boolQueryBuilder.must(QueryBuilders.rangeQuery("update_time").gte(startDate).lte(endDate));
        }
        searchSourceBuilder.query(boolQueryBuilder);
        request.source(searchSourceBuilder);
        //分页查询
        request.source().sort("update_time", SortOrder.DESC).from(pageIndex).size(pageSize);
        SearchResponse response = null;
        try {
            response = esClient.search(request, RequestOptions.DEFAULT);
        } catch (IOException e) {
            log.error("查询es订单数据出错");
            e.printStackTrace();
        }
        assert response != null;
        SearchHits hits = response.getHits();
        if (hits == null) {
            return new Pager<>();
        }
        //返回数据处理
        List<OrderViewModel> orderViewModels = Arrays.stream(hits.getHits()).map(hit -> {
            String hitResult = hit.getSourceAsString();
            OrderViewModel order = new OrderViewModel();
            JsonNode jsonNode = null;
            try {
                jsonNode = JsonUtil.getTreeNode(hitResult);
            } catch (JsonProcessingException e) {
                log.error("es订单数据转换失败");
                e.printStackTrace();
            }
            order.setId(jsonNode.findPath("id").asLong());
            order.setStatus(jsonNode.findPath("status").asInt());
            order.setBill(jsonNode.findPath("bill").asInt());
            order.setOwnerId(jsonNode.findPath("owner_id").asInt());
            order.setPayType(jsonNode.findPath("pay_type").asText());
            order.setOrderNo(jsonNode.findPath("order_no").asText());
            order.setInnerCode(jsonNode.findPath("inner_code").asText());
            order.setSkuName(jsonNode.findPath("sku_name").asText());
            order.setSkuId(jsonNode.findPath("sku_id").asLong());
            order.setPayStatus(jsonNode.findPath("pay_status").asInt());
            order.setBusinessName(jsonNode.findPath("business_name").asText());
            order.setBusinessId(jsonNode.findPath("business_id").asInt());
            order.setRegionId(jsonNode.findPath("region_id").asLong());
            order.setRegionName(jsonNode.findPath("region_name").asText());
            order.setPrice(jsonNode.findPath("price").asInt());
            order.setAmount(jsonNode.findPath("amount").asInt());
            order.setAddr(jsonNode.findPath("addr").asText());
            order.setOpenId(jsonNode.findPath("open_id").asText());
            order.setCreateTime(LocalDateTime.parse(jsonNode.findPath("create_time").asText(),DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
            order.setUpdateTime(LocalDateTime.parse(jsonNode.findPath("update_time").asText(),DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
            return order;
        }).collect(Collectors.toList());
        Pager<OrderViewModel> orderViewModelPager = new Pager<>();
        orderViewModelPager.setPageIndex(pageIndex);
        orderViewModelPager.setPageSize(pageSize);
        orderViewModelPager.setTotalPage(hits.getHits().length);
        orderViewModelPager.setTotalCount(hits.getTotalHits().value);
        orderViewModelPager.setCurrentPageRecords(orderViewModels);
        return orderViewModelPager;
    }

    @Override
    public List<Long> getTop10Sku(Integer businessId) {

        SearchRequest searchRequest = new SearchRequest("order");
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        //查询最近三个月商品
        RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("update_time");
//        rangeQueryBuilder.gte(Date.from(LocalDateTime.now().plusMonths(-3).atZone(ZoneId.systemDefault()).toInstant()));
//        rangeQueryBuilder.lte(Date.from(LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant()));
        rangeQueryBuilder.gte(LocalDateTime.now().plusMonths(-3).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        rangeQueryBuilder.lte(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        boolQueryBuilder.must(rangeQueryBuilder);

        boolQueryBuilder.must(QueryBuilders.termQuery("business_id",businessId));
        searchSourceBuilder.query(boolQueryBuilder);

        TermsAggregationBuilder orderAgg = AggregationBuilders.terms("sku").field("sku_id")
                .subAggregation(AggregationBuilders.count("count").field("sku_id"))
                .order(BucketOrder.aggregation("count", false))
                .size(10);
        searchSourceBuilder.aggregation(orderAgg);
        searchRequest.source(searchSourceBuilder);

        try {
            SearchResponse response = esClient.search(searchRequest, RequestOptions.DEFAULT);
            Aggregations aggregations = response.getAggregations();
            if (aggregations == null) return new ArrayList<>();
            Terms terms = aggregations.get("sku");
            List<? extends Terms.Bucket> buckets = terms.getBuckets();
            return buckets.stream().map(b->Long.parseLong(b.getKey().toString())).collect(Collectors.toList());
        } catch (IOException e) {
            e.printStackTrace();
            return new ArrayList<>();
        }
    }

    @Override
    public long getTotalBill(LocalDateTime start, LocalDateTime end) {
        return orderCollectService.getTotalBill(start,end);
    }
}
